﻿using System.Windows.Controls;
using System.Windows;
using System.Windows.Media.Imaging;
using System;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
namespace DeepEarth.Map.Core.Clustering
{
    [TemplatePart(Name = PART_IconContainer, Type = typeof(Grid))]
    [TemplatePart(Name = PART_OverlayContainer, Type = typeof(Canvas))]
    [TemplatePart(Name = PART_AdditionalOverlaysContainer, Type = typeof(Panel))]
    public class MapItemAdorner : Control, IScalable, ISelfPositioning
    {
        const string PART_IconContainer = "PART_IconContainer";
        const string PART_OverlayContainer = "PART_OverlayContainer";
        const string PART_AdditionalOverlaysContainer = "PART_AdditionalOverlaysContainer";
        Grid iconContainer;
        Canvas overlayContainer;
        Panel allOverlaysContainer;
        
        private FrameworkElement content;
        public FrameworkElement Content
        {
            get
            {
                return content;
            }
            set
            {
                content = value;

                //ContentIsScalable = content is IScalable;
                IScalable s = content as IScalable;
                ContentIsScalable = (s != null && !s.MaintainConstantSize);
                ContentIsSelfPositioning = content is ISelfPositioning;

                Layer.SetPositionOrigin(this, Layer.GetPositionOrigin(content));

                if (iconContainer != null)
                {
                    InsertContent();
                }

                BindPosition(content);
            }
        }

        void BindPosition(FrameworkElement positionedElement)
        {
            // Binding Path for an attached property source non trivial in Silverlight.
            string bindingDefinition = "<Binding xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:core=\"clr-namespace:DeepEarth.Map.Core;assembly=DeepEarth.Map.Core\" Path=\"(core:Layer.Position)\" />";
            Binding positionBinding = XamlReader.Load(bindingDefinition) as Binding;
            positionBinding.Source = positionedElement;

            this.SetBinding(Layer.PositionProperty, positionBinding);
        }

        private FrameworkElement overlay;
        public FrameworkElement Overlay
        {
            get
            {
                return overlay;
            }
            set
            {
                overlay = value;

                OverlayScaleTransform = new ScaleTransform();
                overlay.RenderTransform = OverlayScaleTransform;

                if (overlayContainer != null)
                {
                    InsertOverlay();
                }
            }
        }

        // Used to postion labels in the static layer
        private Point overlayPixelOffset;
        public Point OverlayPixelOffset
        {
            get
            {
                return overlayPixelOffset;
            }
            set
            {
                overlayPixelOffset = value;

                if (Overlay != null)
                {
                    PositionOverlay();
                }
            }
        }

        private void PositionOverlay()
        {
            Canvas.SetLeft(Overlay, OverlayPixelOffset.X);
            Canvas.SetTop(Overlay, OverlayPixelOffset.Y);
        }

        private Action<double, double> ScaleAction = (scale, appliedScaleTransform) => { };

        bool contentIsScalable;
        bool ContentIsScalable
        {
            get
            {
                return contentIsScalable;
            }
            set
            {
                contentIsScalable = value;
                if (value)
                {
                    ScaleAction = (scale, appliedScale) =>
                        {
                            ((IScalable)Content).ApplyScale(scale, appliedScale);
                        };

                }
                else
                {
                    ScaleAction = (scale, appliedScale) => { };
                }
            }
        }


        private Action<Point, double> SelfPositionAction = (Point offset, double maxBound) => { };

        bool contentIsSelfPositioning;
        public bool ContentIsSelfPositioning
        {
            get
            {
                return contentIsSelfPositioning;
            }
            set
            {
                contentIsSelfPositioning = value;
                if (value)
                {
                    ((ISelfPositioning)Content).LocationToPoint = this.LocationToPoint;
                    SelfPositionAction = ((ISelfPositioning)Content).Refresh;
                }
                else
                {
                    SelfPositionAction = (Point p, double d) => { };
                }
            }
        }

        private void InsertContent()
        {
            iconContainer.Children.Clear();

            if (content.Parent != null && content.Parent != iconContainer)
            {
                Panel parent = content.Parent as Panel;
                parent.Children.Clear();
            }

            iconContainer.Children.Add(content);
        }

        private void InsertOverlay()
        {
            overlayContainer.Children.Clear();

            if (overlay.Parent != null && overlay.Parent != overlayContainer)
            {
                Panel parent = overlay.Parent as Panel;
                parent.Children.Clear();
            }

            overlayContainer.Children.Add(overlay);
        }

        public MapItemAdorner()
        {
            this.DefaultStyleKey = typeof(MapItemAdorner);

            OverlayScaleTransform = new ScaleTransform();
        }

        public ScaleTransform OverlayScaleTransform { get; set; }

        TranslateTransform allOverlaysTranslateTransform;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            iconContainer = (Grid)GetTemplateChild(PART_IconContainer);

            overlayContainer = (Canvas)GetTemplateChild(PART_OverlayContainer);

            allOverlaysContainer = (Panel)GetTemplateChild(PART_AdditionalOverlaysContainer);
            allOverlaysTranslateTransform = new TranslateTransform();
            allOverlaysContainer.RenderTransform = allOverlaysTranslateTransform;

            if (content != null)
            {
                InsertContent();    
            }

            if (overlay != null)
            {
                InsertOverlay();
                PositionOverlay();
            }

        }

        //public void Clear()
        //{
        //    if (iconContainer != null)
        //    {
        //        iconContainer.Children.Clear();
        //    }

        //    if (overlayContainer != null)
        //    {
        //        overlayContainer.Children.Clear();
        //    }
        //}

        public void ApplyScale(double scale, double appliedScaleTransform)
        {
            ScaleAction(scale, appliedScaleTransform);
        }

        public bool MaintainConstantSize { get; set; }

        public void Refresh(Point offset, double maxBound)
        {
            SelfPositionAction(offset, maxBound);
        }

        private Func<DeepEarth.Core.Location, Point> locationToPoint;
        public Func<DeepEarth.Core.Location, Point> LocationToPoint
        {
            get
            {
                return locationToPoint;
            }
            set
            {
                locationToPoint = value;

                if (ContentIsSelfPositioning)
                {
                    ((ISelfPositioning)Content).LocationToPoint = value;
                }
            }
        }

        // Invoked by the synchronizer to notify the control that a position origin offset has been applied
        // Used to keep the ClusterSize textbox in the approximate location.
        public void PositionOriginOffsetApplied(double offsetX, double offsetY)
        {
            if (allOverlaysTranslateTransform != null)
            {
                allOverlaysTranslateTransform.X = offsetX;
                allOverlaysTranslateTransform.Y = offsetY;
            }
        }

        public event EventHandler<MapItemAdornerEventArgs> VisibilityChanged;

        public void OnVisibilityChanged()
        {
            if (VisibilityChanged != null)
            {
                VisibilityChanged(this, new MapItemAdornerEventArgs { IsVisible = this.Visibility == Visibility.Visible });
            }
        }
    }

    public class MapItemAdornerEventArgs : EventArgs
    {
        public bool IsVisible { get; set; }
    }
}
